#pragma once

#include <iostream>
#include <cstring>
#include <string>
#include <cerrno>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>
#include <functional>

#include "Log.hpp"
#include "InetAddr.hpp"


//设置状态
enum Status //
{
    NEW,          // 新建状态，就是单纯的连接
    CONNECTING,   // 正在连接，仅仅方便查询conn状态
    CONNECTED,    // 连接或者重连成功
    DISCONNECTED, // 重连失败
    CLOSED        // 连接失败，经历重连，无法连接
};


//默认参数
const int gretry_interval = 1;  //重连时间
const int gmax_retries = 5; //最大重连次数


class ClientConnection
{
public:
    ClientConnection(std::string serverip, uint16_t serverport, int retry_interval = gretry_interval, int max_retries = gmax_retries)
        : _serverip(serverip),
          _serverport(serverport),
          _retry_interval(retry_interval),
          _max_retries(max_retries),
          _sockfd(-1),
          _status(Status::NEW)
    {
    }

    // 连接
    void Connect()
    {
        // 获取fd
        _sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            std::cout << "_sockfd error..." << std::endl;
            return;
        }

        struct sockaddr_in server_addr;
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(_serverport);
        server_addr.sin_addr.s_addr = inet_addr(_serverip.c_str());

        // client 不需要显示的进行bind, tcp是面向连接的, connect 底层会自动进行bind
        int n = ::connect(_sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
        if (n < 0)
        {
            std::cout << "connect failed" << std::endl;
            Disconnect();
            return;
        }

        _status = Status::CONNECTED;
    }
    
    // 重连
    void Reconnect()
    {
        _status = Status::CONNECTING;

        int count = 0;
        while (count < _max_retries)
        {
            std::cout << "Reconnect :  " << count + 1 << std::endl;
            Connect();

            if (_status == Status::CONNECTED)
            {
                return;
            }

            sleep(_retry_interval);
            ++count;
        }

        _status = Status::CLOSED;
    }

    //释放socket
    void Disconnect()
    {
        if (_sockfd > 0)
        {
            close(_sockfd);
            _sockfd = -1;
            _status = Status::CLOSED;
        }
    }

    //进行读写
    void Process()
    {
        // echo client
        std::string message;
        while (true)
        {
            char inbuffer[1024];
            std::cout << "input message: ";
            std::getline(std::cin, message);

            int n = ::write(_sockfd, message.c_str(), message.size());
            if (n > 0)
            {
                int m = ::read(_sockfd, inbuffer, sizeof(inbuffer));
                if (m > 0)
                {
                    inbuffer[m] = 0;
                    std::cout << inbuffer << std::endl;
                }
                else if (m == 0)
                {
                    _status = Status::DISCONNECTED;
                    return;
                }
                else
                {
                    _status = Status::CLOSED;
                    return;
                }
            }
            else
            {
                _status = Status::CLOSED;
                return;
            }
        }
    }

    Status GetStatus()
    {
        return _status;
    }

private:
    std::string _serverip; // 服务端ip
    uint16_t _serverport;  // 服务端端口号
    int _sockfd;           //套接字
    int _retry_interval; // 重试时间间隔
    int _max_retries;    // 重试次数
    Status _status; //状态描述
};

class TcpClient
{
public:
TcpClient(uint16_t serverport, const std::string &serverip) : _conn(serverip,serverport)
    {
    }
    void Execute()
    {
        while (true)
        {
            //根据状态来调用不同函数
            switch (_conn.GetStatus())
            {
            case Status::NEW:
                _conn.Connect();
                break;
            case Status::CONNECTED:
                std::cout << "连接成功, 开始进行通信." << std::endl;
                _conn.Process();
                break;
            case Status::DISCONNECTED:
                std::cout << "连接失败或者对方掉线，开始重连." << std::endl;
                _conn.Reconnect();
                break;
            case Status::CLOSED:
                _conn.Disconnect();
                std::cout << "重连失败, 退出." << std::endl;
                return; // 退出
            default:
                break;
            }
        }
    }
    ~TcpClient()
    {
    }

private:
    ClientConnection _conn; // 简单组合起来即可
};
